package Folding;

use 5.008008;
use strict;
use warnings;
use Term::ANSIColor;

#version 001;


require Exporter;
our @ISA = qw(Exporter);

# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.


# This allows declaration	use Folding ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.

our %EXPORT_TAGS = ( 'all' => [ qw( 

) ] );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = qw( &run_folding &set_folding_parameter );

our $VERSION = '0.01';


my @ct_names;
my @zones;

# Preloaded methods go here.
my (%folding_arguments) = (
	'virus'				=> 'none',
	'start_temp' 		=> 35, ## Temperature of wich it will start the folding
	'end_temp'			=> 41, ## Temperature of wich it will start the folding
	'seq_file'			=> '', ## It is the name of the RNA or DNA sequence ( a c g t )
	'zones_file'		=> '', ## It is the name of the file which contain the zones to analize
	'complement'		=> 0,  ## Set to 1 if the folding should be calculated with the complement.
	'result_package'	=> 0,  ## Name of the result file.
	'not_fold'			=> 0,  ## Do not run the folding prediction programs.
	'not_parse_det'		=> 0,  ## Do not get information from detail files.
	'folding_method'	=> 'unafold', ## Folding programm prediction, could be UNAFold or mfold
	'seq_comp_file'		=> 'none', 
	'working_dir'		=> '',
	'home_dir'			=> '',
	'result_file'		=> '',
    'seq_dir'   		=> '',
    'zone_file_dir'     => '',
 );

my (%detail_paramenter) = (
	'structure_to_add'	=> 1,
	'nucleo_initial'	=> 0,
	'nucleo_end'		=> 0
);


#######  BEGIN Exported Symbol #####
### Main function which start the wrapper execution.
sub run_folding
{
    my $daemon=shift;
    
    use File::Path;
    print color 'underline cyan';
    print ">>"x15;
    print " FuDePan Wrapper Foling is starting ";
    print "<<"x15;
    print "\n";
    print color 'reset';
    
    forking() unless (!$daemon);
    mkpath ($folding_arguments{home_dir},1);
    chdir $folding_arguments{home_dir};
    mkpath ($folding_arguments{working_dir},1);
    chdir $folding_arguments{working_dir};
    
    `cp -f "$folding_arguments{seq_dir}/$folding_arguments{seq_file}" .`;
    `cp -f "$folding_arguments{zone_file_dir}/$folding_arguments{zones_file}" $folding_arguments{zones_file}`;
    
    make_complementary() unless ( !$folding_arguments{complement} );
    read_zones_file();
    calc_secondary_structure();

    my $results_name;
    pack_results() unless ($folding_arguments{result_package});
    chdir $folding_arguments{home_dir};
    print color 'underline cyan';
    print ">>"x15;
    print " Foling finished ";
    print "<<"x15;
    print "\n";
    print color 'reset'
    
}

sub parse_dir_and_name_file
{
    my ($full_path,$extension)=@_;
    $full_path=~ m/\W*(\w+)(\.$extension)$/;
    my $name=$1;
    $full_path=$`;
    return ($name,$full_path);
}

sub set_folding_parameter
{
	my ($sTemp,$eTemp,$sFile,$zFile,$comp,$packed,$notFold,$notDet,$fMethod,$resume,$homeDir)=@_;

    if ($homeDir eq '')
    {
        $homeDir=`pwd`;
        chomp $homeDir;
    }
    $folding_arguments{home_dir}=$homeDir;

	if ($resume)
    {
        load_parameter_from_file();
    }
    else
    {
        
        if($sTemp != 35)
    	{
    			$folding_arguments{start_temp}=$sTemp;
    	}
    	if($eTemp != 41)
    	{
    			$folding_arguments{end_temp}=$eTemp;
    	}
    	if($sFile ne '')
    	{
            my $extention="seq";
            ($folding_arguments{virus},$folding_arguments{seq_dir})=parse_dir_and_name_file($sFile,$extention);
            $folding_arguments{seq_file}=$folding_arguments{virus}."\.".$extention;
            $ct_names[0]=$folding_arguments{virus};
            $folding_arguments{result_file}=$folding_arguments{virus}."\.results";
            
            if ($folding_arguments{seq_dir} eq '')
            {
                $folding_arguments{seq_dir}=$folding_arguments{home_dir};
            }
        }
    	if($zFile ne '')
    	{
            my $extention="txt";
            ($folding_arguments{zones_file},$folding_arguments{zone_file_dir})=parse_dir_and_name_file($zFile,$extention);
            $folding_arguments{zones_file}.="\.".$extention;
            
            if ($folding_arguments{zone_file_dir} eq '')
            {
                $folding_arguments{zone_file_dir}=$folding_arguments{home_dir};
            }
        }
    	if($comp == 1)
    	{
    			$folding_arguments{complement}=$comp;
    			$ct_names[1]=$ct_names[0]."_compl";
    			$folding_arguments{seq_comp_file}=$ct_names[1].".seq";
    	}
    	if($packed == 1)
    	{
    			$folding_arguments{result_package}=$packed;
    	}
    	if($notFold == 1)
    	{
    			$folding_arguments{not_fold}=$notFold;
    	}
    	if($notDet == 1)
    	{
    			$folding_arguments{not_parse_det}=$notDet;
    	}
    	if($fMethod eq 'mfold')
    	{
    			$folding_arguments{folding_method}=$fMethod;
    	}
    	
        $folding_arguments{working_dir}=$folding_arguments{home_dir}."/".$folding_arguments{virus}."/".$folding_arguments{folding_method};
    	dump_parameter();
    
    }
}

sub forking
{
    print color 'reverse cyan';
    #print "Starting Folding as Daemon\n";
    my ($pid);
    if ( $pid = daemonize() ) 
    {
        exit 0;
    }
    print "Starting Folding as Daemon\n";
    print color 'reset';

}
#######  END Exported Symbol #####
sub daemonize
{
    use Carp;
    my($pid);
    
    FORK: 
    {
        if (defined($pid = fork)) 
        {
            return $pid;
        } 
        elsif ($! =~ /No more process/) 
        {
            sleep 5;
            redo FORK;
        } else 
        {
            croak "Can't fork: $!";
        }
    }
}


sub check_parameter()
{

    if ( ( $folding_arguments{seq_file} eq '' ) || ( $folding_arguments{zones_file} eq ''))
    {
        print color 'bold blink red';
        print "There are not enough parameter set for run a folding\n";
        print color 'reset';
    }
    
return;
}

sub dump_parameter
{
    use Data::Dumper;

    my $HANDLERESULTS;
    open_file(">$folding_arguments{home_dir}/parameter",\$HANDLERESULTS);
    print color 'bold cyan';
    print "Generating a parameterfile\n";
    #my $var_dumper;
    $Data::Dumper::Terse = 1; 
    $Data::Dumper::Useqq = 1;
    $Data::Dumper::Indent = 0;
    print $HANDLERESULTS Dumper(\%folding_arguments);

    #while ( my ($key, $value) = each(%folding_arguments) ) 
    #{
    #    $var_dumper.="$key:$value,";
    #}
    #print $HANDLERESULTS $var_dumper;
    print "Parameter file generated\n";
    print color 'reset';
    close $HANDLERESULTS;
}

sub load_parameter_from_file
{
    use Data::Dumper;

    my $HANDLERESULTS;
    open_file("$folding_arguments{home_dir}/parameter",\$HANDLERESULTS);
    print color 'bold cyan';
    print "Loading Parameter from File\n";
    my $line = <$HANDLERESULTS>;
    $line =~ s/[{|}|\"]//g;
    my @var_para= split /\,/,$line;
    foreach my $var (@var_para)
    {
        my @variables=split /\s*\=\>\s*/,$var;
        $folding_arguments{$variables[0]}=$variables[1];
       print "folding_arguments{$variables[0]}=$variables[1]\n";
    }
    $ct_names[0]=$folding_arguments{virus};
    if ($folding_arguments{complement})
    {
        $ct_names[1]=$ct_names[0]."compl";
    }
    print "Parameter loaded\n";
    print color 'reset';
        
}


####### BEGIN Presentation Function ######
sub pack_results
{
    my $sTemp=$folding_arguments{start_temp};
	my $eTemp=$folding_arguments{end_temp};
	
	my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
	$year+=1900;
	my $data_time="$year$mon$mday$hour$min";
	my $fname=$ct_names[0]."_".$sTemp."_".$eTemp."_".$data_time;
	print color 'bold green';
    print "Packing the Resutls and foldings of $fname\n";
	my $files_arguments=$folding_arguments{result_file};
    $files_arguments=$folding_arguments{seq_file};
    
    for(my $temp = $sTemp; $temp <= $eTemp; $temp++)
	{
		$files_arguments=join(" ",$files_arguments,$temp);
	}
	if ($folding_arguments{complement})
	{
		$files_arguments=join(" ",$files_arguments,$folding_arguments{seq_comp_file});
	}
	
	my $tar_name=$fname."\.tgz";
	my $tar_cmd="tar -zcf";
	my $rm_cmd="rm -rf";
	`$tar_cmd $tar_name $files_arguments`;
	`$rm_cmd $files_arguments`;
	print "The result were packed with the name of $fname\n";
    print color 'reset';
}

sub check_result_file
{
    my ($tempToCheck)=shift;
    my $result=1;
    my $HANDLERESULTS;

   	unless(open_file("$folding_arguments{working_dir}/$folding_arguments{result_file}",\$HANDLERESULTS))
    {
        open_file(">$folding_arguments{working_dir}/$folding_arguments{result_file}",\$HANDLERESULTS);
        print color 'bold blink blue';
        print "Generating a new $folding_arguments{working_dir}/$folding_arguments{result_file}\n";
        print color 'reset';
        print_header($HANDLERESULTS,((@zones)-1));
    }
    else
    {
        my @file=<$HANDLERESULTS>;
        if (grep /^$tempToCheck\,/,@file)
        {
           $result=0;
        }
    }
     
    close ($HANDLERESULTS);

return $result;
}


sub print_header
{
    my ($HANDLERESULT,$last_index)=@_;
    my $print_head="TEMP(Celsius),ENERGIA_DEL_CT";

    for(my $zone_name_index = 0; $zone_name_index < $last_index; $zone_name_index += 3)
    {
        my $index=($zone_name_index / 3)+1;
        $print_head=join (',',$print_head,"NUM_ZONA_$index","ENERGIA_CALCULADA_$index","NUC_APAREADOS_$index","SS-SCORE_$index");
    }
    print $HANDLERESULT "$print_head\n";
}

####### END Presentation Function ######

sub calc_secondary_structure
{

   	foreach my $name_seq (@ct_names)
	{
        # run mfold iff --notfold was NOT specified in the command line
        run_temp_folding_temp_secuency($name_seq);
    }
	return;
}

####### BEGIN -- Call of Specific Folding Program ########

sub running_program
{
	my ($new_file_name,$temp)=@_;
	my @result;

	if ($folding_arguments{folding_method} eq "unafold")
	{
		@result = `UNAFold.pl -t $temp $new_file_name 2>&1`;
        #@result=run_unafold($new_file_name,$temp);
	}
	elsif ($folding_arguments{folding_method} eq "mfold")
	{
		@result = `mfold SEQ=$new_file_name T=$temp 2>&1`;
        #@result=run_mfold($new_file_name,$temp);
	}

return @result;
}
####### END  -- Call of Specific Folding Program ########


sub run_temp_folding_temp_secuency
{
use Shell qw(cp);

    my ($fName) = @_;
    
	my $sTemp=$folding_arguments{start_temp};
	my $eTemp=$folding_arguments{end_temp};
	print color 'bold blink green';
	print "Running Folding with $folding_arguments{folding_method} from temp: $sTemp to temp: $eTemp, $fName\n";
    print color 'reset';
    check_result_file("TEMP(Celsius)");
   	
	for(my $temp = $sTemp; $temp <=$eTemp; $temp++)
    {
        print color 'bold green';
        print "Processing at temperature: $temp with $folding_arguments{folding_method} \n";
    	if (check_result_file($temp))
        {
            mkdir $temp;
            chdir $temp;
        
    		my $new_file_name=$fName."_".$temp."\.seq";
            #cp("$folding_arguments{working_dir}/$fName.seq", "./$new_file_name");
            `cp -f "$folding_arguments{working_dir}/$fName.seq" ./$new_file_name`;
            my @result=running_program($new_file_name,$temp) unless ($folding_arguments{not_fold});
    		if (ct_analyzer_for_temp($temp,$fName) == 1 )
            {
                my $output_file=$fName."_"."mfold_output\.txt";
                my $OUT;
                open_file(">$output_file",\$OUT);
                print $OUT @result;
                files_to_rm($fName,$temp);
                close($OUT);
                chdir "..";
                print "Temperature: $temp was folded with $folding_arguments{folding_method} \n";
            }
            else
            {
                print color 'blink bold red';
                print "Temperature: $temp was not processed\n";
                print color 'reset';
            }
        }
        else
        {
            print color 'blink yellow';
            print "Temperature: $temp was not folded. The temp is present in the $folding_arguments{result_file}\n";
        }
    }
	print color 'reset';
    return;
}

sub ct_analyzer_for_temp
{
    my ($temp,$name_seq)=@_;
    my $HANDLERESULTS;

    open_file(">>$folding_arguments{working_dir}/$folding_arguments{result_file}",\$HANDLERESULTS);

    my $det_file="$folding_arguments{working_dir}/".$temp."/".$name_seq."_".$temp."\.det";
    my $ct_file="$folding_arguments{working_dir}/".$temp."/".$name_seq."_".$temp."_1.ct";
    my $ss_file="$folding_arguments{working_dir}/".$temp."/".$name_seq."_".$temp.".ss-count";

    my ($wasFolded,$total,$energy)=get_data_header_file($ct_file);
    if ($wasFolded == 1)
    {
        my $arrayNucleoApp=$temp.",".$energy;

        make_zones(\$ct_file,\$ss_file,\$arrayNucleoApp,\@zones,\$det_file,$total);  

        print $HANDLERESULTS "$arrayNucleoApp\n";
        print $HANDLERESULTS "-"x100; 
        print $HANDLERESULTS "\n";
    }
    close $HANDLERESULTS;

return $wasFolded;    
}

sub make_zones
{
	my ($ct_file,$ss_file,$arrayNucleoApp,$zones_array,$det_file,$total)=@_;
	my $last_index = scalar(@$zones_array)-1;

	for(my $zone_name_index = 0; $zone_name_index < $last_index; $zone_name_index += 3)
	{
		my $nucleo_app;
		my $ss_score=0;
		my $init_zone=$$zones_array[$zone_name_index+1];
		my $end_zone=$$zones_array[$zone_name_index+2];
		if ($$ct_file =~ m/\_compl/)
		{ 
            if ($end_zone == 0)
			{
				$init_zone=1;
			}
			else
			{
				$init_zone=$total-$end_zone+1;
				$end_zone=$total-$$zones_array[$zone_name_index+1]+1;
			}
        }
		
        my $newEnergy=0; 
		
        get_energy_from_det_file($det_file,\$newEnergy,$init_zone,$end_zone) unless($folding_arguments{not_parse_det}) ;
        read_nucleotidos_apareaos($ct_file,\$nucleo_app,$init_zone,$end_zone);
        sum_zone_no_apareados($ss_file,\$ss_score,$init_zone,$end_zone);
        $$arrayNucleoApp=join(',',$$arrayNucleoApp,$$zones_array[$zone_name_index],$newEnergy,$nucleo_app,$ss_score);
        
	}

}
####################################

## The function create a compler for either RNA or  DNA sequence ###
sub make_complementary
{
   	my %complem = ( "a" => "t",
					"t" => "a",
					"c" => "g",
					"g" => "c",
					" " => " "
					);

	my $HANDLE;
	my $HANDLECOMPL;
	print color 'blink blue';
    print "Generating the COMPLEMENTARY secuence\n";
	open_file("$folding_arguments{seq_file}",\$HANDLE);
	open_file(">$folding_arguments{seq_comp_file}",\$HANDLECOMPL);

	my $charCount=0;
	my $lineNucleotidos="";
	
	while (read($HANDLE,my $char,1))
	{	
		if ($char =~ m/\b(a|t|c|g)/i)
		{ 
			$lineNucleotidos=join('',$complem{lc($char)},$lineNucleotidos);
			$charCount++;
			if($charCount == 60)
			{
				$charCount=0;
				$lineNucleotidos=join('',"\n",$lineNucleotidos);
			}	
		}
	}
	print $HANDLECOMPL "$lineNucleotidos\n";
	close($HANDLE);
	close($HANDLECOMPL);
    
	print "The COMPLEMENTARY secuence finished\n";
    print color 'reset';
    return;
}
###############################
sub det_paser_file
{
	my $file_name=shift;
	my $DET_FILE ;
	my $energy=0;
    if (open_file($file_name,\$DET_FILE))
	{
    	while(<$DET_FILE>)
    	{
    	#Stack:           ddG =  -0.23 External closing pair is t(    26)-g(    48)
    		if(m/Structure\s+[$detail_paramenter{structure_to_add}][\s|\:]/../Structure\s+[$detail_paramenter{structure_to_add_plus}][\s|\:]/)
    		{
    			if(m/^\w+\W*\w*\:\s+ddG\s\=\s+(\W\d\.\d+).+\(\s*(\d+)\)\-/)
    			{
    				my $nucleo_to_compare =$2+0;
    				if ($detail_paramenter{nucleo_initial} <= $nucleo_to_compare && ( $detail_paramenter{nucleo_end} == 0 ||  $nucleo_to_compare <= $detail_paramenter{nucleo_end}))
    				{
    					$energy += 0 + $1;
    				}
    			}
    		}
    	}
    }
	return $energy;
}

sub get_energy_from_det_file{
my ($det_file,$energy,$init_zone,$end_zone)=@_;
	
        my $struct_number=1;
        
		$detail_paramenter{structure_to_add}=$struct_number;
		$detail_paramenter{structure_to_add_plus}=$detail_paramenter{structure_to_add}+1;
		$detail_paramenter{nucleo_initial}=$init_zone;
		$detail_paramenter{nucleo_end}=$end_zone;

		my $ener=det_paser_file($$det_file);
		$$energy=$ener;
	
}
#########################################3

sub read_zones_file{
	# An array to hold the zones. The structure is:
    # (zone_name1, initial_nucleotide1, final_nucleotide1, zone_name2, initial_nucl2, final_nucl2, etc)
	my $FH;
	print color 'blink underscore magenta';
    open_file($folding_arguments{zones_file},\$FH);
    print "Parsing the Zone Files\n";
    while( my $line = <$FH> )
	{
        chomp $line;
		# match a line of the form:
	    #zone_name, init_nucleotide, final_nucleotide
		# and stores the values in $1, $2 and $3
        if ($line =~ m/^\s*(.*?)\s*,\s*(\d+)\s*,\s*(\d+).*$/)
		{
	        if ( ($2 >= $3 and $3 != 0))
			{
                die "malformed line \"$line\"!!\n";
            }
	        my @tmp_arr = ($1, $2, $3);
            push(@zones, @tmp_arr);
            
        } 
		elsif ($line =~ m/\s*/) 
		{
            # linea vacia: skip
        } 
	}
	close($FH);
    print "Zone Files has parsed\n";
    print color 'reset';
    }

###########################################

sub sum_zone_no_apareados
{
	my ($file_name,$returnValue,$iniNucleo,$endNucleo)=@_;

	my $HANDLE;
	if(open_file($$file_name,\$HANDLE))
	{
        my $DIGITS="\(\\d{1,5}\)";
    	my $PATTER="\^\\s*".$DIGITS."\\s+\(\\d{1,3}\)\\s+\\w{1}\$";		
    	my $PATTESTRUCT="^\\s*\(\\d{1,3}\)\$";	
    	my $addNotApp=0;
    	my $nucleo=1;
    	my $number_struct=0;
    	while ( defined(local $_= <$HANDLE>) && ($nucleo != $endNucleo))
    	{
    		if(m/$PATTER/)
    		{
    			$nucleo=$1;
    			my $appare=$2;
    			if($nucleo >= $iniNucleo)
    			{
    				$addNotApp+=$appare;	
    			}	
    		}elsif (m/$PATTESTRUCT/) 
    		{
    			$number_struct=$1;
    		}
    	}
    	close ($HANDLE);
    	if($endNucleo == 0 )
    	{
    		$endNucleo=$nucleo;
    	}
    	my $zone= ($endNucleo-$iniNucleo+1) * $number_struct;
    	my $output= ($addNotApp)/$zone;
    	print "---> not apareados $output <--\n";
    	$$returnValue=$output;
    }
return ;
}
########################################

sub read_nucleotidos_apareaos
{
	my ($file_name,$returnValue,$iniNucleo,$endNucleo)=@_;

	my $HANDLE;
    my $countApp=0;

	if(open_file($$file_name,\$HANDLE))
    {
    	#1       g       0       2       0       1       0       2
        my $DIGITS="\(\\d{1,6}\)";
        my $PATTER="\^\\s*".$DIGITS."\\s+\\w\\s+".$DIGITS."\\s+".$DIGITS."\\s+".$DIGITS."\\s+".$DIGITS;
    	
        my $nucleo=1;
        my @line=("");
    	
    	while ( defined(local $_= <$HANDLE>) && ($nucleo != $endNucleo))
    	{
    		if(m/$PATTER/)
    		{
    			$nucleo=$1;
    			my $appare=$4;
    			if($nucleo >= $iniNucleo)
     	    	{
    				if ($appare != 0)
    	      		{
    					$countApp++;
    				}
    			}	
    	    }
    	}

    	close ($HANDLE);
	}
    $$returnValue=$countApp;

    return;
}

sub get_data_header_file
{
	my ($file_name)=@_;
	my $HANDLE;
	my @data_header=(0,0,0,0);
    my $result=0;
    if (open_file($file_name,\$HANDLE))
    {
    # 	3411 dG =  -1266.90 junin_s_24   
    	$_=<$HANDLE>;
    	close($HANDLE);
    	s/^\s+|\s+$//g;
    	s/\s+/ /g;
    	@data_header= split/\s/,$_; 
    	$data_header[3]+=0;
        $result=1;
    }
    
 return ($result,$data_header[0],$data_header[3]);
}

sub open_file
{
    use Carp;

    my ($file_name,$HANDLE)=@_;
    my $status=1;
    unless (open($$HANDLE,$file_name))
    {
        print color 'bold blink red';
        print "can't open $file_name: \n";
        print color 'reset';
        $status=0;
    }
    
return $status;
}

sub files_to_rm
{
 use Shell qw(rm);

    my ($file_name,$temp)=@_;
	my $big_ct_file = $file_name."_".$temp."\.ct";
	rm("-f", "$big_ct_file");
    #`rm -f $big_ct_file`;
	rm("-f", "*.sav");
    #`rm -f *.sav`;		
    rm("-f", "*.rnaml");
	#`rm -f *.rnaml`;	
    rm("-f","*.plot");
	#`rm -f *.plot`;
    rm("-f","*.sav");
	#`rm -f *.sav`;		
	rm("-f","*.sav");
    #`rm -f *.ps`;	
    rm("-f","*.out");
	#`rm -f *.out`;		
}

1;
__END__
# Below is stub documentation for your module. You'd better edit it!

=head1 NAME

Folding Batch - Perl extension for automatic excecution of mFold and UNAFold program for nucleic acid folding and hybridization prediction. 

=head1 SYNOPSIS

  use Folding; ##  
  
  Set the Parameter into the Moduel
  Folding::set_folding_parameter(
		$initial_temp,
		$final_temp,
		$seq_file,
		$zones_file,
		$complement,
		$must_not_package,
		$must_not_fold,
		$not_run_parser_det,
		$folding_type);
		
  Run the folding program
  
  Folding::run_folding();
	 

=head1 DESCRIPTION

Folding script was developed by FuDePan Folding team with the aim to generated a wrapper of the Secodary Program prediction. 
The Foloding.pm provide an mechanims to execute an run both mfold and unfold program with differentes temperatures and parse differents files in order to get specific Energy Value.

=head2 EXPORT

&run_folding 
&set_folding_parameter 


=head1 SEE ALSO

Mention other useful documentation such as the documentation of
related modules or operating system documentation (such as man pages
in UNIX), or any relevant external documentation such as RFCs or
standards.

If you have a mailing list set up for your module, mention it here.

If you have a web site set up for your module, mention it here.

=head1 AUTHORs

Agustin March, E<lt>agustin.march@fudepan.org.arE<gt>
Maximiliano Combina, E<lt>maxicombina@fudepan.org.arE<gt>
Daniel Gutson, E<lt>daniel.gutson@fudepan.org.arE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2008 by ammarch

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.8 or,
at your option, any later version of Perl 5 you may have available.


=cut
